package helper;


import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.impl.DynamicEObjectImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.ocl.OCL;
import org.eclipse.ocl.OCLInput;
import org.eclipse.ocl.ParserException;
import org.eclipse.ocl.Query;
import org.eclipse.ocl.ecore.EcoreEnvironmentFactory;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.helper.OCLHelper;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Namespace;
import org.eclipse.uml2.uml.OpaqueExpression;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.xtend.typesystem.emf.EcoreUtil2;
import org.quality4spl.fm.CVMModelWrapper;
import org.quality4spl.fm.FMWrapper;

import SimpleQM.QElement;
import SimpleQM.QoS;
import SimpleQM.QoSCharacteristic;
import SimpleQM.QoSType;
import SimpleQM.SimpleQMFactory;
import SimpleQM.SimpleQMPackage;
import SimpleQM.impl.QoS_latencyImpl;

public class Helper {
	
	private static Log log = LogFactory.getLog(Helper.class);
	
	public static CVMModelWrapper model = null;

	public final static Object getTagValue(Element element, Stereotype st,
			String propName) {
		if (element.hasValue(st, propName)) {
			Object o = element.getValue(st, propName);
			return o;
		}
		return null;

	}
	
	public final static boolean isFeatureSelected(String feature){
		return isFeatureSelected(feature, model);
	}
	
	public final static boolean isFeatureSelected(String feature, FMWrapper model){
		return model.getSelectedFeatures().contains(feature);
	}
	
	public final static void remove(Element e){
		EcoreUtil.remove(e);
	
	}
	
	public final static void eliminateDanglingReferences(EObject model) {
		Iterator<EObject> i = model.eAllContents();
		while (i.hasNext()) {
			EObject o = i.next();
			for (EReference r : o.eClass().getEAllReferences())
				if (!r.isContainment() && o.eIsSet(r)) {
					if (r.isMany()) {
						List<EObject> l = (List<EObject>) o.eGet(r);
						for (int j = l.size() - 1; j >= 0; j--)
							if (l.get(j).eResource() == null) 
								try{
									l.remove(j);	
								}catch (UnsupportedOperationException e) {
									//ignore
								}
					} else {
						EObject x = (EObject) o.eGet(r);
						if (x.eResource() == null) {
							o.eUnset(r);
						}
					}
				}
		}
	}
	
	public static final String genID(){
		return String.valueOf(System.currentTimeMillis());
	}
	
	public static final String evaluateOCL(org.eclipse.ocl.ecore.Constraint cons){
		return evaluateOCL(cons.getSpecification().getBodyExpression());
	}
	
	public static final String evaluateOCL(OCLExpression cons){
		return evaluateOCL(null, cons).toString();
	}
	
	public static final Object evaluateOCL(EObject ctx, Object cons){
		OCL<?, EClassifier, ?, ?, ?, ?, ?, ?, ?, org.eclipse.ocl.ecore.Constraint, EClass, EObject> ocl 
	      = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE);
		QoSCharacteristic self = null;
		OCLExpression<EClassifier> query;
		if(cons instanceof String){
			query = parseOCL(ctx.eClass(), ocl, (String) cons);
		} else if(cons instanceof OCLExpression){
			self = (QoSCharacteristic) ((OCLExpression)cons).eContainer().eContainer().eContainer();
			query = (OCLExpression<EClassifier>) cons;
		} else 
			throw new RuntimeException("Invalid constraint type");
		Query<EClassifier, EClass, EObject> eval 
	      = (Query<EClassifier, EClass, EObject>) ocl.createQuery(query);
		Object result;
		if(self != null)
			result = eval.evaluate(self);
		else
			result = eval.evaluate();
		if(result instanceof DynamicEObjectImpl){
			if(((DynamicEObjectImpl)result).eClass().getName().equals("Invalid_Class"))
				throw new RuntimeException("evaluation error");
		}
	    return result;	
	}
	
	private static OCLExpression<EClassifier> parseOCL(
					EClass ctx,
					OCL<?, EClassifier, ?, ?, ?, ?, ?, ?, ?, org.eclipse.ocl.ecore.Constraint,EClass, EObject> ocl,
					String cons) {
		try {
		    OCLHelper<EClassifier, ?, ?, org.eclipse.ocl.ecore.Constraint> helper = ocl.createOCLHelper();
		    helper.setContext(ctx);
			return helper.createQuery(cons);
		} catch (ParserException e) {
			e.printStackTrace();
		}
		return null;
	}

	public static final QElement findQElement(EObject ctx, String qElement){
		TreeIterator<EObject> iter = getRoot(ctx).eAllContents();
		while (iter.hasNext()){
			EObject e = iter.next();
			if(e instanceof QElement)
				if( ((QElement)e).getName().equals(qElement) )
					return (QElement)e;
		}
		return null;
	}
	
	private static EObject getRoot(EObject ctx) {
		if(ctx == null || ctx.eContainer() == null)
			return ctx;
		return getRoot(ctx.eContainer());
	}

	public static final void mapQoS(String cons, QElement ctx, String qosType){
		OCL<?, EClassifier, ?, ?, ?, ?, ?, ?, ?, org.eclipse.ocl.ecore.Constraint, EClass, EObject> ocl 
	      = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE);
		
	    OCLInput input = new OCLInput("package SimpleQM "+ cons +" endpackage");
	    List<org.eclipse.ocl.ecore.Constraint> constraints = null;
		try {
			constraints = ocl.parse(input);
		} catch (ParserException e) {
			e.printStackTrace();
		}
		org.eclipse.ocl.ecore.Constraint consObj = constraints.get(0);
    	String context = consObj.getSpecification().getContextVariable().getType().getName();
        OCLExpression<EClassifier> body = consObj.getSpecification().getBodyExpression();
        log.info(consObj.getName()+":" +" "+ body);
    	QoSCharacteristic charact = createQoSCharacteristic(context);
    	charact.setConstraint((org.eclipse.ocl.ecore.Constraint) EcoreUtil.copy(consObj));	    	
		QoS qos = SimpleQMFactory.eINSTANCE.createQoS();
		qos.setCharacteristic(charact);
		if(qosType.equalsIgnoreCase("QoSOffered"))
			qos.setType(QoSType.OFFERED);
		if(qosType.equalsIgnoreCase("QoSRequired"))
			qos.setType(QoSType.REQUIRED);
		ctx.getQos().add(qos);
	}
	

	public static final QoSCharacteristic createQoSCharacteristic(String name){
		if(name.equals(SimpleQMPackage.Literals.QO_SAVAILABILITY.getName())){
			return SimpleQMFactory.eINSTANCE.createQoS_availability();
		}
		else if(name.equals(SimpleQMPackage.Literals.QO_SCOSTS.getName())){
			return SimpleQMFactory.eINSTANCE.createQoS_costs();
		}
		else if(name.equals(SimpleQMPackage.Literals.QO_SDISASTER_RECOVERY.getName())){
			return SimpleQMFactory.eINSTANCE.createQoS_disasterRecovery();
		}
		else if(name.equals(SimpleQMPackage.Literals.QO_SLATENCY.getName())){
			return SimpleQMFactory.eINSTANCE.createQoS_latency();
		}
		else if(name.equals(SimpleQMPackage.Literals.QO_SSECURITY.getName())){
			return SimpleQMFactory.eINSTANCE.createQoS_security();
		}
		throw new RuntimeException("'"+ name +"' is no QoSCharacteristic.");
	}
}
